Цель исследования - разобраться, как ведут себя пользователи мобильного приложения
Задачи исследования:
Описание данных:
Каждая запись в логе — это действие пользователя, или событие.
EventName — название события;
DeviceIDHash — уникальный идентификатор пользователя;
EventTimestamp — время события;
ExpId — номер эксперимента: 246 и 247 — контрольные группы, а 248 — экспериментальная.
Ход исследования:
1.1 Импорт необходимых библиотек
import pandas as pd
import datetime as dt
import plotly.express as px
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from plotly import graph_objects as go
from scipy import stats as st
import math as mth
import plotly.offline as pyo
1.2 Загрузка и чтение данных из csv-файла в датафрейм c помощью библиотеки pandas
logs = pd.read_csv('/datasets/logs_exp.csv', sep='\t')
#создание функции для чтение первичной информации для датасетов
def data_full_info(dataset):
print (dataset.info())
print('\033[1m' + 'Количество дубликатов в таблице:' + '\033[0m', dataset.duplicated().sum())
return (dataset.head(10))
data_full_info(logs)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 244126 entries, 0 to 244125
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 EventName 244126 non-null object
1 DeviceIDHash 244126 non-null int64
2 EventTimestamp 244126 non-null int64
3 ExpId 244126 non-null int64
dtypes: int64(3), object(1)
memory usage: 7.5+ MB
None
Количество дубликатов в таблице: 413
| EventName | DeviceIDHash | EventTimestamp | ExpId | |
|---|---|---|---|---|
| 0 | MainScreenAppear | 4575588528974610257 | 1564029816 | 246 |
| 1 | MainScreenAppear | 7416695313311560658 | 1564053102 | 246 |
| 2 | PaymentScreenSuccessful | 3518123091307005509 | 1564054127 | 248 |
| 3 | CartScreenAppear | 3518123091307005509 | 1564054127 | 248 |
| 4 | PaymentScreenSuccessful | 6217807653094995999 | 1564055322 | 248 |
| 5 | CartScreenAppear | 6217807653094995999 | 1564055323 | 248 |
| 6 | OffersScreenAppear | 8351860793733343758 | 1564066242 | 246 |
| 7 | MainScreenAppear | 5682100281902512875 | 1564085677 | 246 |
| 8 | MainScreenAppear | 1850981295691852772 | 1564086702 | 247 |
| 9 | MainScreenAppear | 5407636962369102641 | 1564112112 | 246 |
logs['EventName'].unique()
array(['MainScreenAppear', 'PaymentScreenSuccessful', 'CartScreenAppear',
'OffersScreenAppear', 'Tutorial'], dtype=object)
Датасет содержит в себе информацию о действиях пользователей, или событиях. После изучения общей информации о датасете видно, что пропуски в записях отсутствуют, однако имеются явные дубликаты, которые необходимо удалить в хоже предобработки данных. Кроме того, нужно будет изменить названия столбцов и типы данных для комфортного дальнейшего анализа.
2.1 Изменение названий столбцов
logs.columns = ['event_name', 'user_id', 'event_time', 'exp_id']
2.2 Удаление дубликатов
logs = logs.drop_duplicates().reset_index(drop=True)
2.3 Изменение типа данных и добавление столбцов
logs['date'] = pd.to_datetime(logs['event_time'], unit='s')
logs['day'] = logs['date'].astype('datetime64[D]')
2.4 Проверка датасета на корректность после обработки
logs
| event_name | user_id | event_time | exp_id | date | day | |
|---|---|---|---|---|---|---|
| 0 | MainScreenAppear | 4575588528974610257 | 1564029816 | 246 | 2019-07-25 04:43:36 | 2019-07-25 |
| 1 | MainScreenAppear | 7416695313311560658 | 1564053102 | 246 | 2019-07-25 11:11:42 | 2019-07-25 |
| 2 | PaymentScreenSuccessful | 3518123091307005509 | 1564054127 | 248 | 2019-07-25 11:28:47 | 2019-07-25 |
| 3 | CartScreenAppear | 3518123091307005509 | 1564054127 | 248 | 2019-07-25 11:28:47 | 2019-07-25 |
| 4 | PaymentScreenSuccessful | 6217807653094995999 | 1564055322 | 248 | 2019-07-25 11:48:42 | 2019-07-25 |
| ... | ... | ... | ... | ... | ... | ... |
| 243708 | MainScreenAppear | 4599628364049201812 | 1565212345 | 247 | 2019-08-07 21:12:25 | 2019-08-07 |
| 243709 | MainScreenAppear | 5849806612437486590 | 1565212439 | 246 | 2019-08-07 21:13:59 | 2019-08-07 |
| 243710 | MainScreenAppear | 5746969938801999050 | 1565212483 | 246 | 2019-08-07 21:14:43 | 2019-08-07 |
| 243711 | MainScreenAppear | 5746969938801999050 | 1565212498 | 246 | 2019-08-07 21:14:58 | 2019-08-07 |
| 243712 | OffersScreenAppear | 5746969938801999050 | 1565212517 | 246 | 2019-08-07 21:15:17 | 2019-08-07 |
243713 rows × 6 columns
2.5 Проверка на наличие повторяющихся пользователей в группах
group_246 = logs[logs['exp_id'] == '246']['user_id']
group_247 = logs[logs['exp_id'] == '247']['user_id']
group_248 = logs[logs['exp_id'] == '248']['user_id']
groups = logs.query('user_id in @group_246 and user_id in @group_247 and user_id in @group_248')
display(groups['user_id'].unique())
print('Количество повторяющихся пользователей в группах:', groups['user_id'].nunique())
array([], dtype=int64)
Количество повторяющихся пользователей в группах: 0
Предобработка данных завершена, можно приступить к работе.
3.1 Сколько всего событий в логе?
print('Уникальных событий в логе:', logs['event_name'].nunique())
print('Общее количество событий в логе:', logs['event_name'].count())
print('Количество каждого события:\n',
logs['event_name'].value_counts())
Уникальных событий в логе: 5 Общее количество событий в логе: 243713 Количество каждого события: MainScreenAppear 119101 OffersScreenAppear 46808 CartScreenAppear 42668 PaymentScreenSuccessful 34118 Tutorial 1018 Name: event_name, dtype: int64
3.2 Cколько всего пользователей в логе?
print('Количество уникальных пользователей в логе:', logs['user_id'].nunique())
for i in logs['exp_id'].unique():
exp_users = logs[logs["exp_id"]==i]['user_id'].nunique()
print(f'В группе {i} - {exp_users} уникальных пользователей')
Количество уникальных пользователей в логе: 7551 В группе 246 - 2489 уникальных пользователей В группе 248 - 2542 уникальных пользователей В группе 247 - 2520 уникальных пользователей
3.3 Сколько в среднем событий приходится на пользователя?
event_per_user = logs.groupby('user_id')['event_name'].count()
event_per_user.describe()
count 7551.000000 mean 32.275593 std 65.154219 min 1.000000 25% 9.000000 50% 20.000000 75% 37.000000 max 2307.000000 Name: event_name, dtype: float64
В среднем на одного пользователя приходится 32 события.
Однако медианное значение составляет - 20 событий на пользователя.
3.4 Анализ периода исследования
print('Дата начала событий в логе:', logs['date'].min())
print('Дата окончания событий в логе:', logs['date'].max())
print('Период событий в логе:', logs['date'].max() - logs['date'].min())
Дата начала событий в логе: 2019-07-25 04:43:36 Дата окончания событий в логе: 2019-08-07 21:15:17 Период событий в логе: 13 days 16:31:41
event_per_day = logs.pivot_table(index='day', columns='exp_id', values='event_name', aggfunc='count') \
.sort_values('day')
fig = px.bar(event_per_day,
template='plotly_white', title='Распределение количество событий в зависимости от времени в разрезе групп',
labels=dict(day="дата", value="количество событий", variable="номер эксперимента"))
fig.show()
Исходя из визуализации столбчатой диаграммы, мы можем с уверенностью сказать, что мы располагаем неполными данными в течение всего периода исследования. До 31 июля включительно данные являются неполными, следовательно их нужно отбросить. Для дальнейшей работы следует оставить период с 1 августа по 7 августа 2019 г.
old_logs= logs.query('day <= "2019-07-31"')
new_logs = logs.query('day >= "2019-08-01"')
new_logs.reset_index(drop=True)
| event_name | user_id | event_time | exp_id | date | day | |
|---|---|---|---|---|---|---|
| 0 | Tutorial | 3737462046622621720 | 1564618048 | 246 | 2019-08-01 00:07:28 | 2019-08-01 |
| 1 | MainScreenAppear | 3737462046622621720 | 1564618080 | 246 | 2019-08-01 00:08:00 | 2019-08-01 |
| 2 | MainScreenAppear | 3737462046622621720 | 1564618135 | 246 | 2019-08-01 00:08:55 | 2019-08-01 |
| 3 | OffersScreenAppear | 3737462046622621720 | 1564618138 | 246 | 2019-08-01 00:08:58 | 2019-08-01 |
| 4 | MainScreenAppear | 1433840883824088890 | 1564618139 | 247 | 2019-08-01 00:08:59 | 2019-08-01 |
| ... | ... | ... | ... | ... | ... | ... |
| 240882 | MainScreenAppear | 4599628364049201812 | 1565212345 | 247 | 2019-08-07 21:12:25 | 2019-08-07 |
| 240883 | MainScreenAppear | 5849806612437486590 | 1565212439 | 246 | 2019-08-07 21:13:59 | 2019-08-07 |
| 240884 | MainScreenAppear | 5746969938801999050 | 1565212483 | 246 | 2019-08-07 21:14:43 | 2019-08-07 |
| 240885 | MainScreenAppear | 5746969938801999050 | 1565212498 | 246 | 2019-08-07 21:14:58 | 2019-08-07 |
| 240886 | OffersScreenAppear | 5746969938801999050 | 1565212517 | 246 | 2019-08-07 21:15:17 | 2019-08-07 |
240887 rows × 6 columns
print('Количество событий после очищения данных:', new_logs['event_name'].count())
print('Количество отброшенных событий:', logs['event_name'].count() - new_logs['event_name'].count())
print('Доля отброшенных событий:', "{0:.2f}%".format((old_logs['event_name'].count() / logs['event_name'].count()) * 100))
Количество событий после очищения данных: 240887 Количество отброшенных событий: 2826 Доля отброшенных событий: 1.16%
print('Количество пользователей после очищения данных:', new_logs['user_id'].nunique())
print('Количество отброшенных пользователей:', logs['user_id'].nunique() - new_logs['user_id'].nunique())
print('Доля отброшенных пользователей:', "{0:.2f}%".format(((logs['user_id'].nunique() - new_logs['user_id'].nunique())/logs['user_id'].nunique()) *100))
Количество пользователей после очищения данных: 7534 Количество отброшенных пользователей: 17 Доля отброшенных пользователей: 0.23%
display(new_logs.groupby('exp_id').agg(nunique=('user_id', 'nunique')))
display(new_logs.groupby('exp_id').agg(count=('user_id', 'count')))
| nunique | |
|---|---|
| exp_id | |
| 246 | 2484 |
| 247 | 2513 |
| 248 | 2537 |
| count | |
|---|---|
| exp_id | |
| 246 | 79302 |
| 247 | 77022 |
| 248 | 84563 |
Отбросив неполные данные мы потеряли 1.16% событий и 0.23% пользователей, что в последующем не должно оказать существенного влияния на ход и результаты исследования. Данные с которыми дальше будет проводиться работа:
Распределение пользователей по группам эксперимента:
4.1 Какие события есть в логах, как часто они встречаются?
events_count = new_logs.groupby('event_name').agg(count = ('user_id', 'count')).sort_values('count', ascending=False).reset_index()
events_count
| event_name | count | |
|---|---|---|
| 0 | MainScreenAppear | 117328 |
| 1 | OffersScreenAppear | 46333 |
| 2 | CartScreenAppear | 42303 |
| 3 | PaymentScreenSuccessful | 33918 |
| 4 | Tutorial | 1005 |
plt.figure(figsize=(15, 7))
plt.title('Количество событий в логах')
sns.barplot(y=events_count['event_name'], x=events_count['count'], palette='Set2', orient='h')
for i, v in enumerate(events_count['count'].values):
plt.text(v + 800, i - 0.1, str(v), color='black', fontsize = 10)
plt.text(v + 800, i + 0.2, '({:.2%})'.format(v / events_count['count'].sum()), color='black', fontsize = 10)
plt.xlabel('')
plt.ylabel('')
sns.despine()
plt.show()
В логах встречается 5 типов событий:
Чаще всего проиходит просмотр главного экрана(117328 событий - около 49%). Далее по частоте совершения идут: просмотр страницы каталога, просмотр корзины, а после страница успешной оплаты. Реже всего пользователи проходят этап обучение (1005 событий - меньше 1%)
4.2 Cколько пользователей совершали каждое из событий?
user_per_event = new_logs.groupby('event_name').agg(users=('user_id','nunique')).sort_values('users', ascending=False).reset_index()
plt.figure(figsize=(15, 7))
plt.title('Количество пользователей, совершивших события')
sns.barplot(y=user_per_event['event_name'], x=user_per_event['users'], palette='Set2', orient='h')
for i, v in enumerate(user_per_event['users'].values):
plt.text(v + 200, i - 0.1, str(v), color='black', fontsize = 10)
plt.xlabel('')
plt.ylabel('')
sns.despine()
plt.show()
user_per_event
| event_name | users | |
|---|---|---|
| 0 | MainScreenAppear | 7419 |
| 1 | OffersScreenAppear | 4593 |
| 2 | CartScreenAppear | 3734 |
| 3 | PaymentScreenSuccessful | 3539 |
| 4 | Tutorial | 840 |
7419 пользователей просмотрели главную экран.
4593 пользователей просмотрели каталог товаров.
3734 пользователей просмотрели страницу корзины,с добавленными товарами.
3539 пользователей увидели страницу успешно совершенной покупки.
Минимальное количество пользователей: 840, - прошли обучение.
4.3 Последовательность событий
Основываясь на рассмотренных данных можно предположить следующую цепочку событий:
Прохождение обучения (Tutorial) соверша.т минимальное количество пользователей. Оно выбивается из логической последовательни. Предположительно, данный шаг не является обязательным.
4.4 Анализ воронки событий
Уберем событие Tutorial перед построением воронки
user_per_event = user_per_event.query('event_name != "Tutorial"')
user_per_event
| event_name | users | |
|---|---|---|
| 0 | MainScreenAppear | 7419 |
| 1 | OffersScreenAppear | 4593 |
| 2 | CartScreenAppear | 3734 |
| 3 | PaymentScreenSuccessful | 3539 |
pyo.init_notebook_mode()
fig = go.Figure()
fig = go.Figure(go.Funnel(
y = user_per_event['event_name'],
x = user_per_event['users'],
textinfo = "value+percent initial+percent previous",
opacity = 0.65))
fig.update_layout(title='Воронка')
После первого шага (просмотр главного экрана) приступили к следующему шагу (просмотр каталога товаров) 62% пользователей. От второго события к просмотру корзины с товарами перешли 81% пользователей. До успешного прохождения оплаты после просмотра корзины дошли 95% пользователей.
Больше всего пользователей теряется на втором шаге, то есть при переходе с главной страницы к каталогу товаров. Такие пользователи составляют 38% . Причинами такой доли потери клиентов могли стать:
Около 48% процентов пользователей доходят до последнего события воронки, проходя все этапы от просмотра главного экрана до успешной оплаты заказа.
Учитывая, что до второго этапа доходят лишь 62% пользователей, данный показатель неплох.
5.1 Количество пользователей в каждой экспериментальной группе
print('Количество уникальных пользователей:', new_logs['user_id'].nunique())
for i in new_logs['exp_id'].unique():
nexp_users = new_logs[new_logs["exp_id"]==i]['user_id'].nunique()
print(f'В группе {i} - {nexp_users} уникальных пользователей')
Количество уникальных пользователей: 7534 В группе 246 - 2484 уникальных пользователей В группе 247 - 2513 уникальных пользователей В группе 248 - 2537 уникальных пользователей
5.2 Проверка выборок 246 и 247
Проведем проверку равенства долей с помощью z-test.
H0: между долями нет значимой разницы
H1: между долями есть значимая разница
alpha = 0.05
unique_1 = new_logs.query('exp_id == 246')['user_id'].nunique()
unique_2 = new_logs.query('exp_id == 247')['user_id'].nunique()
total_1 = new_logs.query('exp_id == 246')['user_id'].count()
total_2 = new_logs.query('exp_id == 247')['user_id'].count()
p1 = unique_1/total_1
p2 = unique_2/total_2
p_combined = (unique_1 + unique_2) / (total_1 + total_2)
difference = p1-p2
z_value = difference / mth.sqrt(p_combined * (1 - p_combined) * (1/unique_1 + 1/unique_2))
distr = st.norm(0, 1)
p_value = (1 - distr.cdf(abs(z_value))) * 2
print('p-значение: ', p_value)
if p_value < alpha:
print('Отвергаем нулевую гипотезу: между долями есть значимая разница')
else:
print(
'Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы разными'
)
p-значение: 0.7933578138021369 Не получилось отвергнуть нулевую гипотезу, нет оснований считать группы разными
По результатам тестирования для всех событий разница не оказалось значимой, поэтому эти группы можно считать контрольными.
5.3 Выберем самое популярное событие. Посчитаем число пользователей, совершивших это событие в каждой из контрольных групп. Посчитаем долю пользователей, совершивших это событие. Проверим, будет ли отличие между группами статистически достоверным. Проделаем то же самое для всех других событий
#Создадим отдельные датасеты с группами
group_246 = new_logs.query('exp_id == 246 and event_name != "Tutorial"')
group_247 = new_logs.query('exp_id == 247 and event_name != "Tutorial"')
group_248 = new_logs.query('exp_id == 248 and event_name != "Tutorial"')
newest_logs = new_logs.query('event_name != "Tutorial"')
#самое популярное событие в группе 246
group_246.pivot_table(index = 'event_name', values = 'user_id', aggfunc = 'nunique').sort_values(by = 'user_id', ascending = False)
| user_id | |
|---|---|
| event_name | |
| MainScreenAppear | 2450 |
| OffersScreenAppear | 1542 |
| CartScreenAppear | 1266 |
| PaymentScreenSuccessful | 1200 |
group_247.pivot_table(index = 'event_name', values = 'user_id', aggfunc = 'nunique').sort_values(by = 'user_id', ascending = False)
| user_id | |
|---|---|
| event_name | |
| MainScreenAppear | 2476 |
| OffersScreenAppear | 1520 |
| CartScreenAppear | 1238 |
| PaymentScreenSuccessful | 1158 |
Самое популярное событие - MainScreenAppear. По нему будет совершаться проверка гипотезы.
H0: между группами нет значимой разницы
H1: между группами есть значимая разница
#критический уровень статистической значимости
alpha = 0.05
#количсетво уникальных пользователей совершивших событие в каждой группе
unique_user_event = np.array([group_246[group_246['event_name'] == 'MainScreenAppear']['user_id'].nunique(), group_247[group_247['event_name'] == 'MainScreenAppear']['user_id'].nunique()])
#количество уникальных пользователей в группах
total_unique = np.array([group_246['user_id'].nunique(), group_247['user_id'].nunique()])
# пропорция успехов в первой группе:
p1 = unique_user_event[0]/total_unique[0]
# пропорция успехов во второй группе:
p2 = unique_user_event[1]/total_unique[1]
# пропорция успехов в комбинированном датасете:
p_combined = (unique_user_event[0] + unique_user_event[1]) / (total_unique[0] + total_unique[1])
# разница пропорций в датасетах
difference = p1 - p2
# считаем статистику в ст.отклонениях стандартного нормального распределения
z_value = difference / mth.sqrt(p_combined * (1 - p_combined) * (1/unique_user_event[0] + 1/unique_user_event[1]))
# задаем стандартное нормальное распределение (среднее 0, ст.отклонение 1)
distr = st.norm(0, 1)
#Так как распределение статистики нормальное, вызовем метод cdf()
#Саму статистику возьмём по модулю методом abs() — чтобы получить правильный результат независимо от её знака.
#Это возможно, потому что тест двусторонний. По этой же причине удваиваем результат:
p_value = (1 - distr.cdf(abs(z_value))) * 2
print('p-значение: ', p_value)
if p_value < alpha:
print('Отвергаем нулевую гипотезу: между долями есть значимая разница')
else:
print('Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными')
p-значение: 0.7543284477026952 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными
Значимой разницы между группами по событию MainScreenAppear - нет.
Проверка по всем событиям
Также применим поправку Бонферрони для устранения эффекта множественных сравнений, так как есть шанс совершить ошибку первого рода (отвергнуть нулевую гипотезу, когда она на самом деле верна).
Выбрали значение 16, так как будет совершено 4 проверки групп по 4 событиям.
def z_test_check(g1, g2, event_name, alpha):
#количсетво уникальных пользователей совершивших событие в каждой группе
unique_user_event = np.array([g1[g1['event_name'] == event_name]['user_id'].nunique(), g2[g2['event_name'] == event_name]['user_id'].nunique()])
#количество уникальных пользователей в группах
total_unique = np.array([g1['user_id'].nunique(), g2['user_id'].nunique()])
# пропорция успехов в первой группе:
p1 = unique_user_event[0]/total_unique[0]
# пропорция успехов во второй группе:
p2 = unique_user_event[1]/total_unique[1]
# пропорция успехов в комбинированном датасете:
p_combined = (unique_user_event[0] + unique_user_event[1]) / (total_unique[0] + total_unique[1])
# разница пропорций в датасетах
difference = p1 - p2
# считаем статистику в ст.отклонениях стандартного нормального распределения
z_value = difference / mth.sqrt(p_combined * (1 - p_combined) * (1/unique_user_event[0] + 1/unique_user_event[1]))
# задаем стандартное нормальное распределение (среднее 0, ст.отклонение 1)
distr = st.norm(0, 1)
#Так как распределение статистики нормальное, вызовем метод cdf()
#Саму статистику возьмём по модулю методом abs() — чтобы получить правильный результат независимо от её знака.
#Это возможно, потому что тест двусторонний. По этой же причине удваиваем результат:
p_value = (1 - distr.cdf(abs(z_value))) * 2
#Применим поправку Бонферрони
bonferroni_alpha = alpha / 16
print(event_name)
print('p-значение: ', p_value)
if p_value < alpha:
print('Отвергаем нулевую гипотезу: между долями есть значимая разница')
else:
print('Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными')
print()
#создадим список с уникальными событиями:
event_name_array = newest_logs['event_name'].unique()
#создадим цикл, который будет подставлять в функцию уникальные значения собитый:
for event_name in event_name_array:
z_test_check(group_246, group_247, event_name, 0.05)
MainScreenAppear p-значение: 0.7543284477026952 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными OffersScreenAppear p-значение: 0.3656033033916035 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными CartScreenAppear p-значение: 0.3940666380052331 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными PaymentScreenSuccessful p-значение: 0.2782106304210834 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными
Значимой разницы между контрольными группами по событиям - нет. Разбиение на группы сработало корректно.
5.4 Сравнение с экспериментальной группой
Так как для теста контрольной и экспериментальной группы не нужна высокая точность, то для дальнейших экспериментов выберем самый стандартный уровень значимости равный 5%.
проверка контрольной группы 246 и экспериментальной 248
for event_name in event_name_array:
z_test_check(group_246, group_248, event_name, 0.05)
MainScreenAppear p-значение: 0.34233944977927466 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными OffersScreenAppear p-значение: 0.3312582326385347 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными CartScreenAppear p-значение: 0.21798592844886056 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными PaymentScreenSuccessful p-значение: 0.3950230284265204 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными
Значимой разницы между группами 246 и 248 нет.
проверка контрольной группы 247 и экспериментальной 248
for event_name in event_name_array:
z_test_check(group_247, group_248, event_name, 0.05)
MainScreenAppear p-значение: 0.5227396775479345 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными OffersScreenAppear p-значение: 0.9481746290642528 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными CartScreenAppear p-значение: 0.7046835535938734 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными PaymentScreenSuccessful p-значение: 0.8125473059339452 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными
Значимой разницы между группами 247 и 248 нет.
Проверка объединенной конрольной группы и экспериментальной
#объеденим группы методом pd.concat
group_246_247 = pd.concat([group_246, group_247])
for event_name in event_name_array:
z_test_check(group_246_247, group_248, event_name, 0.05)
MainScreenAppear p-значение: 0.352466134886821 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными OffersScreenAppear p-значение: 0.5525365385535834 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными CartScreenAppear p-значение: 0.3552373774407942 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными PaymentScreenSuccessful p-значение: 0.7277272684366058 Не получилось отвергнуть нулевую гипотезу, нет оснований считать доли разными
Значимой разницы между объединенными контрольными группами и экспериментальной нет.
5.5 Выбор уровня значимости
При проверке гипотез между контрольными и экспериментальной группами уровень статистической значимости был выбран 0.05. Это озачает что мы рискуем получить ошибку первого рода в 5% случаев.
И так как совершаем множественную проверку (16 экспериментов), применили поправку Бонферрони.
После изучения результатов исследования, было определено что:
Спасибо, изучу!
В ходе проведения исследования для мобильного приложения по продаже продуктов питания было выполнено:
Исходя из того факта, что множество проведенных экспериментов по каждому из событий между всеми имеющимися группами не выявили статистически значимой разницы между группами, можно утверждать, что изменение шрифтов в приложении интернет-магазина не оказало большого влияния на поведение пользователей. Тестирование можно назвать успешным.